package autotd;

import java.util.*;
import java.io.*;
import java.awt.Point;

// This class will generate many xlint complaints.
public class GameMap {
  private int myWidth, myLength;
  private Map<Point,List<Tower>> towers = new HashMap<Point,List<Tower>>();
  public List<Point> path = new ArrayList<Point>();
  public Set<Point> fastPath = new HashSet<Point>();
  private Map<Point,List<Monster>> monsters = new HashMap<Point,List<Monster>>();
  
  public GameMap(int width, int length) {
    myWidth = width;
    myLength = length;
    for (int x = 0; x < myWidth; x++) {
      for (int y = 0; y < myLength; y++) {
        Point p = new Point(x,y);
        towers.put(p, new ArrayList<Tower>());
        monsters.put(p, new ArrayList<Monster>());
      }
    }
  }

  public void markPath(Point p) {
    path.add(p);
    fastPath.add(p);
  }

  /** Position within the field */
  public boolean isValid(Point p) {
    return (p.x >= 0) && (p.x < myWidth) && (p.y >= 0) && (p.y < myLength);
  }
  
  /** Valid position for towers */
  public boolean isGrass(Point p) {
  	return isValid(p) && !fastPath.contains(p);
  }
  
  /** Valid position for monsters */
  public boolean isPath(Point p) {
    return isValid(p) && fastPath.contains(p);
  }
  
  
  public boolean add(Tower t, Point p) {
    if (!isGrass(p)) {
      return false;
    }
    towers.get(p).add(t);
    t.pos = p;
    return true;
  }

  public boolean remove(Tower t) {
    if (!isGrass(t.pos)) {
      return false;
    }
    return towers.get(t.pos).remove(t);
  }

  public boolean add(Monster m, Point p) {
    if (!isPath(p)) {
      return false;
    }
    monsters.get(p).add(m);
    m.pos = p;
    return true;
  }

  public boolean remove(Monster m) {
    if (!isPath(m.pos)) {
      return false;
    }
    return monsters.get(m.pos).remove(m);
  }
  
  public boolean moveForward(Monster m) {
		remove(m);
		m.progress++;
		if (m.progress>=path.size()) {
			m.passed = true;
			return true;
		}
		add(m,path.get(m.progress));
		return true;
  }

  public List<Tower> getTowerStack(Point p) {
    if (!isValid(p)) return new ArrayList<Tower>();
    return towers.get(p);
  }

  public List<Monster> getMonsterStack(Point p) {
    if (!isValid(p)) return new ArrayList<Monster>();
    return monsters.get(p);
  }
  
  public List<Point> getPath() {
    return path;
  }
  
  public int getWidth() {
    return myWidth;
  }
  
  public int getLength() {
	return myLength;
  }

  public int getHeight(int x, int y) {
	List<Tower> tStack = getTowerStack(new Point(x, y));
	int height = 0;
	for (Tower nextT : tStack) {
		height += nextT.Type.Height;
	}
	return height;
  }

  
  public static int sqDist(Point p1, Point p2) {
  	int dx = p1.x-p2.x;
  	int dy = p1.y-p2.y;
  	return dx*dx+dy*dy;
  }

	public static boolean inRange(Point p1, Point p2, int range) {
		return sqDist(p1,p2) < range * range;
	}







  private static String nextNonZeroLine(BufferedReader input) throws FileNotFoundException, IOException {
    String line = input.readLine();
    while (line != null) {
      if (line.indexOf("#") != -1) {
       line = line.substring(0, line.indexOf("#")).trim();
      }
      if (line.length() > 0) {
        break;
      }
      line = input.readLine();
    }
    return line;
  }
  
  public static GameMap parseFile(File fname) throws FileNotFoundException, IOException {
    GameMap result;
    FileReader infile = new FileReader(fname);
    BufferedReader input = new BufferedReader(infile);
    String[] dimensions = nextNonZeroLine(input).split(" ");
    int w = Integer.parseInt(dimensions[0]);
    int l = Integer.parseInt(dimensions[1]);
    result = new GameMap(w,l);
    int pathlength = Integer.parseInt(nextNonZeroLine(input));
    for (int i = 0; i < pathlength; i++) {
      String[] pathsquare = nextNonZeroLine(input).split(" ");
      int x = Integer.parseInt(pathsquare[0]);
      int y = Integer.parseInt(pathsquare[1]);
      result.markPath(new Point(x,y));
    }
    return result;
  }
}